///////////////////////////////////////////////////////////////
//  Copyright Christopher Kormanyos 2002 - 2011.
//  Copyright 2011 John Maddock. Distributed under the Boost
//  Software License, Version 1.0. (See accompanying file
//  LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt
//
// This work is based on an earlier work:
// "Algorithm 910: A Portable C++ Multiple-Precision System for Special-Function Calculations",
// in ACM TOMS, {VOL 37, ISSUE 4, (February 2011)} (C) ACM, 2011. http://doi.acm.org/10.1145/1916461.1916469

#ifdef _MSC_VER
#define _SCL_SECURE_NO_WARNINGS
#endif

#include <boost/detail/lightweight_test.hpp>
#include <boost/array.hpp>
#include "test.hpp"

#if !defined(TEST_MPF_50) && !defined(TEST_MPF) && !defined(TEST_BACKEND) && !defined(TEST_CPP_DEC_FLOAT) && \
    !defined(TEST_MPFR) && !defined(TEST_MPFR_50) && !defined(TEST_MPFI_50) && !defined(TEST_FLOAT128) &&    \
    !defined(TEST_CPP_BIN_FLOAT)
#define TEST_MPF_50
#define TEST_MPFR_50
#define TEST_MPFI_50
#define TEST_BACKEND
#define TEST_CPP_DEC_FLOAT
#define TEST_FLOAT128
#define TEST_CPP_BIN_FLOAT

#ifdef _MSC_VER
#pragma message("CAUTION!!: No backend type specified so testing everything.... this will take some time!!")
#endif
#ifdef __GNUC__
#pragma warning "CAUTION!!: No backend type specified so testing everything.... this will take some time!!"
#endif

#endif

#if defined(TEST_MPF_50)
#include <nil/crypto3/multiprecision/gmp.hpp>
#endif
#if defined(TEST_MPFR_50)
#include <nil/crypto3/multiprecision/mpfr.hpp>
#endif
#if defined(TEST_MPFI_50)
#include <nil/crypto3/multiprecision/mpfi.hpp>
#endif
#ifdef TEST_BACKEND
#include <nil/crypto3/multiprecision/concepts/mp_number_archetypes.hpp>
#endif
#ifdef TEST_CPP_DEC_FLOAT
#include <nil/crypto3/multiprecision/cpp_dec_float.hpp>
#endif
#ifdef TEST_FLOAT128
#include <nil/crypto3/multiprecision/float128.hpp>
#endif
#ifdef TEST_CPP_BIN_FLOAT
#include <nil/crypto3/multiprecision/cpp_bin_float.hpp>
#endif

template<class T>
void test() {
    std::cout << "Testing type: " << typeid(T).name() << std::endl;
    static const boost::array<const char*, 51u> data = {{
        "0."
        "32137338579537729992840434899278935725496267444911714908205287462634251052210833615032073534299473485492692600"
        "15632213742180143233312505108872350242503748577641280157756840589207758520676819053085988309457147532331948073"
        "97731939742069667287642242866619457381782105773261729381960622773147615378819363468740758813387682084901938279"
        "60565412814349738654540520356903367084438822830345189856017860672463017",
        "0."
        "98628073997374759901559180515206275902189609982907214282364143439537215491113710345866034807991101303183938339"
        "56149955932788695628078974328782448826907229741344093538171537823303139901549612470660431854553954461076015175"
        "69702298010742805454286421220059828579749216062641031868784992605523887137160325077703195504357387675971506443"
        "70033277228635793175246197730488375240713357827246166262087899961098648",
        "0."
        "99999659728082161113542951278514830809677661658635225532792520501703544628367875293677270218317428676793098034"
        "27604239639679756810326388245173281019050773306346733305635677077400259689876299162965304185465326982464287329"
        "11460685353004664733995927512194491877686466252295007051826769300090965452544781998389907030775979718234566712"
        "41141900968900216742636822174010867343154474773040184900973368250708724",
        "0."
        "99999999998464832682000516095943215501291599979205764809762068674965127524865906604370912059896161085319108977"
        "33432934135954788151766561209164605337537937873588006552145390152387235783207736999759875845070169747521389728"
        "85747652543748354308065112579873956264941487948762185846622325523984562720585021732857486585292287273723428303"
        "8190096982410137471391847466112651349436875974689271288261759782261321",
        "0."
        "99999999999999999873145272420997506481719582113194745587077923115938049630578788144877209973035121340072721114"
        "64990650500302215773762906820778949397525329109311974741708703536680512747226155618703318739024344621256722269"
        "60896941528003533705204442513828182176026826918737751724395158496917503554999488345147191869350247748538568288"
        "4154959809285569290940740978684264145737164182111806308430952867505356",
        "0."
        "99999999999999999999999999808009530698473085868402375905639298553395570655367657652348442407895844874434510333"
        "49994294426525006157530320529970966048109743850154174651033567146346966529198814047607015842020039899630884318"
        "52154322516021219399491174689433584626487806653026673700560677010286923710184844555994163838162561520732046818"
        "4414917265299980278164193202897754476112968080358662232635784904263624",
        "0."
        "99999999999999999999999999999999999994678001402208667547629227476877841332331340104328899595708215544552475283"
        "44437918197518545105332713456859236726383195243056599177018396579766073961540714510399202497404647103138524118"
        "06539118550367889505909679030066580564856964735193589787446058785512743647260847070338426158615892306627212421"
        "6158518232588330799027603391275039552776308636189531809031683723259525",
        "0."
        "99999999999999999999999999999999999999999999999997297959129601333599285875622295123373392178311785748756266192"
        "26954752643039860388090399659478676726654102118545764943682017611574275691203710439707807193180604656837503862"
        "24270043017959166632913172863175277378940606461868287916735172501388829485946754083231079551513967737580740762"
        "2028485242969471516812171475098378942187864794497220152019202351747432",
        "0."
        "99999999999999999999999999999999999999999999999999999999999999974873549383104406502206925195252615524115399968"
        "30587326435051159231522052011310872619940247463640019105467860778515384738650636054646551611609757125760437266"
        "41586521430639652050034311318990730685141895762008097164594330576013238513992458315431715656041454807637089336"
        "0020924960120441059315311380109752919823616149973862196846266297710225",
        "0."
        "99999999999999999999999999999999999999999999999999999999999999999999999999999995720505753041593583592215967433"
        "79014313563214857593401545331495211888415913558896516895286943572966865708195832339959764445194449615969154689"
        "76955790316697124952189888348936242638692429787208892585289235015086594142735531982726091483426875473210252810"
        "5919811851193667193380541054520023003326858641189063542651960743457613",
        "0."
        "99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999986650167335148"
        "21204959171836698281165918083005790816030823236096080522860514417075411055633676502699904415882389102672678124"
        "68308442972087996235589222532136909129849165834917847227095275920844376784831211106912732916046488493227168598"
        "6610697222434145553035947750809388321614695213754318979469071722942883",
        "0."
        "99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999"
        "99999923725166281475848329507280218421983592052015199663910769945947736004746744354730051185717515144461482927"
        "87696117866094991476340344341061706866751156257825144909115117822189019766805825304055482958970512463798453229"
        "341693148012213183553546720540880444569824125592343803193018989122964",
        "0."
        "99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999"
        "99999999999999999999999999920180539244323143459416025663362517256166793900312667214543096238752838226369161775"
        "53145600554137487827452946151128684298893783015570473744820255838261282902246463540028932891624000267078343382"
        "7234852858267621807568393874737879853500598862298495727481132883728328",
        "0."
        "99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999"
        "99999999999999999999999999999999999999999999999998470116425957155787153557938061034698436461173594805292722874"
        "13043361426647513626760649834673058348779126650394365917488655417037753763091477099811457334008159482715073700"
        "5158607830579000912020838690230498119348251553312722575955142752845289",
        "0."
        "99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999"
        "99999999999999999999999999999999999999999999999999999999999999999999999999462931048083476382670449719737943481"
        "96393062676062532676570381539224962262548343304336366439663816261243736780010233281969913184131734817345130857"
        "0776544874933173719394861336636728378134900422312720743996219523365098",
        "0."
        "99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999"
        "99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999996546783689"
        "74872675236489661553286028805709743745277477938607490691581938053172949307195923288762021372360715562895952958"
        "4572083388979892309086717786559916703004365603135284141639480887703759",
        "0."
        "99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999"
        "99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999"
        "99999999999999999959333238101964793942399996951958309983756432105658876626996785347947860312289945626290486906"
        "3959564274215899007623362296377022418464303752396325625838280378647335",
        "0."
        "99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999"
        "99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999"
        "99999999999999999999999999999999999999999999999912284271466756712820335558534655912660602919608072184529239910"
        "4475997315344844970601651757832506169948906646054332493537761143729664",
        "0."
        "99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999"
        "99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999"
        "99999999999999999999999999999999999999999999999999999999999999999999999999999996534727205037443579059791706545"
        "8623422102880662825928396981295898014081371503733411536311258910851858",
        "0."
        "99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999"
        "99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999"
        "99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999"
        "9974926240515037403282500845454464621558236030794140169313830052413917",
        "0."
        "99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999"
        "99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999"
        "99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999"
        "9999999999999999999999999999999999999667705192725716176070161727364499",
        "1.",
        "1.",
        "1.",
        "1.",
        "1.",
        "1.",
        "1.",
        "1.",
        "1.",
        "1.",
        "1.",
        "1.",
        "1.",
        "1.",
        "1.",
        "1.",
        "1.",
        "1.",
        "1.",
        "1.",
        "1.",
        "1.",
        "1.",
        "1.",
        "1.",
        "1.",
        "1.",
        "1.",
        "1.",
        "1.",
    }};

    T eg = static_cast<T>(
        "5."
        "77215664901532860606512090082402431042159335939923598805767234884867726777664670936947063291746749514631447249"
        "80708248096050401448654283622417399764492353625350033374293733773767394279259525824709491600873520394816567085"
        "3233151776611528621199501507984793745085705740029921354786146694029604325421519e-1");

    unsigned max_err = 0;
    for (unsigned k = 0; k < data.size(); k++) {
        const T x = eg + k;
        T val = nil::crypto3::multiprecision::tanh(x * x);
        T e = relative_error(val, T(data[k]));
        unsigned err = e.template convert_to<unsigned>();
        if (err > max_err)
            max_err = err;
    }
    std::cout << "Max error was: " << max_err << std::endl;
    BOOST_TEST(max_err < 100);
}

int main() {
#ifdef TEST_BACKEND
    test<
        nil::crypto3::multiprecision::number<nil::crypto3::multiprecision::concepts::number_backend_float_architype>>();
#endif
#ifdef TEST_MPF_50
    test<nil::crypto3::multiprecision::mpf_float_50>();
    test<nil::crypto3::multiprecision::mpf_float_100>();
#endif
#ifdef TEST_MPFR_50
    test<nil::crypto3::multiprecision::mpfr_float_50>();
    test<nil::crypto3::multiprecision::mpfr_float_100>();
#endif
#ifdef TEST_MPFI_50
    test<nil::crypto3::multiprecision::mpfi_float_50>();
    test<nil::crypto3::multiprecision::mpfi_float_100>();
#endif
#ifdef TEST_CPP_DEC_FLOAT
    test<nil::crypto3::multiprecision::cpp_dec_float_50>();
    test<nil::crypto3::multiprecision::cpp_dec_float_100>();
#ifndef SLOW_COMPLER
    // Some "peculiar" digit counts which stress our code:
    test<nil::crypto3::multiprecision::number<nil::crypto3::multiprecision::cpp_dec_float<65>>>();
    test<nil::crypto3::multiprecision::number<nil::crypto3::multiprecision::cpp_dec_float<64>>>();
    test<nil::crypto3::multiprecision::number<nil::crypto3::multiprecision::cpp_dec_float<63>>>();
    test<nil::crypto3::multiprecision::number<nil::crypto3::multiprecision::cpp_dec_float<62>>>();
    test<nil::crypto3::multiprecision::number<nil::crypto3::multiprecision::cpp_dec_float<61, long long>>>();
    test<nil::crypto3::multiprecision::number<nil::crypto3::multiprecision::cpp_dec_float<60, long long>>>();
    test<nil::crypto3::multiprecision::number<
        nil::crypto3::multiprecision::cpp_dec_float<59, long long, std::allocator<char>>>>();
    test<nil::crypto3::multiprecision::number<
        nil::crypto3::multiprecision::cpp_dec_float<58, long long, std::allocator<char>>>>();
#endif
#endif
#ifdef TEST_FLOAT128
    test<nil::crypto3::multiprecision::float128>();
#endif
#ifdef TEST_CPP_BIN_FLOAT
    test<nil::crypto3::multiprecision::cpp_bin_float_50>();
    test<nil::crypto3::multiprecision::number<nil::crypto3::multiprecision::cpp_bin_float<
        35, nil::crypto3::multiprecision::digit_base_10, std::allocator<char>, boost::long_long_type>>>();
#endif
    return boost::report_errors();
}
